package biolabs.mcl

import biolabs.core.{Application, Interaction, UnaryAction}
import biolabs.mcl.syntax._
import biolabs.util.Types

import java.lang.reflect.{ParameterizedType, Type}

object PerformedAction {
	def analyze(ast: PerformedActionAST, declarationOrder: Int, model: ModelDefinition, app: Application): AnalysisResult[PerformedAction] = {

		// analyze the type
		val actionType = SingleType.analyze(ast.actionType, model, model.typeParameterMap, app) match {
			case AnalysisSuccess(t) => t match {
				case u: ActualType => u
				case v: TypeVar => return AnalysisError("Cannot use type variable " + v.name + " as an action type.")
			}
			case AnalysisError(msg) => return AnalysisError(msg)
		}

		// check that action extends UnaryAction or Interaction
		val baseClass = if(classOf[UnaryAction[_, _, _]].isAssignableFrom(actionType.rawType))
			classOf[UnaryAction[_, _, _]]
		else if(classOf[Interaction[_, _, _, _, _]].isAssignableFrom(actionType.rawType))
			classOf[Interaction[_, _, _, _, _]]
		else
			return AnalysisError(actionType.rawType + " which does not extend " + classOf[Interaction[_, _, _, _, _]] + " nor " + classOf[UnaryAction[_, _, _]] + " used as action")

		val baseTypeArgs = actionType.getTypeArgumentsOf(baseClass)

		if(baseClass == classOf[UnaryAction[_, _, _]])
			AnalysisSuccess(new PerformedUnaryAction(ast, declarationOrder, actionType, baseTypeArgs))
		else
			AnalysisSuccess(new PerformedInteraction(ast, declarationOrder, actionType, baseTypeArgs))
	}
}

/** Represents an action that a model performs
  * on itself and eventually its children.
  */
abstract class PerformedAction {
	val ast: PerformedActionAST
	val declarationOrder: Int
	val actionType: ActualType
	val progressType: MultiType
	val resultType: MultiType
	lazy val instantiationCode = "new " + ast.actionType + "(" + ast.initArgs.mkString(", ") + ")"
}

/** Represents an unary action that a model performs on itself. */
class PerformedUnaryAction(
	override val ast: PerformedActionAST,
	override val declarationOrder: Int,
	override val actionType: ActualType,
	typeArgs: Seq[MultiType]
) extends PerformedAction {
	override val progressType = typeArgs(1)
	override val resultType = typeArgs(2)
}

/** Represents an interaction that a model
  * performs on itself and its children.
  */
class PerformedInteraction(
	override val ast: PerformedActionAST,
	override val declarationOrder: Int,
	override val actionType: ActualType,
	typeArgs: Seq[MultiType]
) extends PerformedAction {
	override val progressType = typeArgs(1)
	override val resultType = typeArgs(2)
	val delegateType = typeArgs(3)
}